package com.jeek.jdbc.myCode.homeWork2;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.*;

/**
 * 一、Thread 、 ThreadLocal 、 ThreadLocalMap 三者之间的关系
 * 1.Thread 线程类中
 *          ->  关联关系 ThreadLocal.ThreadLocalMap threadLocals = null;
 * 2.ThreadLocal
 *          ->  静态内部类 static class ThreadLocalMap
 *                  ->  静态内部类 static class Entry extends WeakReference<ThreadLocal<?>>
 *                      键：ThreadLocal变量  值：存储在ThreadLocal变量中的数据
 *
 *  二、ThreadLocal 线程变量的工作原理
 *  ThreadLocal 线程变量通过隐式的，在不同线程内创建独立实例副本避免了实例线程安全的问题。
 *  1.synchronized 同步代码块 、 同步方法 与  Lock锁 ，实现线程安全的原理 ；
 *      本质上都是加锁产生互斥 ， 从而可以控制资源的访问顺序 ， 解决多线程访问并发资源的安全问题。
 *  2.ThreadLocal
 *      线程变量，以自己ThreadLocal线程变量作为键，以存储在线程变量中的数据作为值 ；
 *      “数据隔离” 每个Thread都有自己的实例副本，有且只能自己的当前线程使用，所以不存在争抢， 自然就不存在多线程访问并发资源的安全问题
 *
 *  三、ThreadLocal 如何解决内存泄露的问题
 *  1.为什么会出现内存泄露？ 引用链
 *  2.如何避免内存泄露？
 *          2.1 设计继承弱引用 static class Entry extends WeakReference<ThreadLocal<?>> ，
 *          弱引用拥有更短暂的生命周期，不管内存够不够，都会回收，都会回收它的内存。 但是不能100%解决内存泄露的问题
 *          2.2 可以搭配使用上  tl.remove();
 */
public class C3P0ByThreadLocalUtils {

    private static final ComboPooledDataSource pool = new ComboPooledDataSource("mysql");

    //创建线程变量 ThreadLocal作为键，Connection连接对象作为值
    private static ThreadLocal<Connection> tl = new ThreadLocal<>();

    //获取数据源
    public static DataSource getDataSource() {
        return pool;
    }

    //获取连接对象
    public static Connection getConn(){
        Connection conn = tl.get();
        try {
            if(conn==null || conn.isClosed()){
                conn = pool.getConnection();
                tl.set(conn);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return conn;
    }

    //关闭资源
    public static void close() {
        Connection conn = tl.get();
        try {
            if(conn!=null && !conn.isClosed()){
                //不做物理层面的释放，会归还至连接池中
                conn.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            //为了确保不会出现内存泄露问题
            tl.remove();
        }
    }
}
